import random,time, pickle, sys, traceback

def loadCombatWeights():
  try:
    print "Loading combatweights..."
    r = open("settings/combatweights","r")
    d = pickle.load(r)
    r.close()
    return d
  except:
    return {
      "barehand_min": 1.0,
      "barehand_max": 8.0,
      "barehand_speed": 2.0,
      "crit_mod": 1.5,
      "alacrity_to_dodge": 0.03,
      "brawn_to_dmg": 0.05
    }

combatWeights = loadCombatWeights()

def saveCombatWeights():
  print "Saving combatweights..."
  r = open("settings/combatweights", "w")
  pickle.dump(combatWeights, r)
  r.close()
  return


def combatweights_command( ch, args ):
  """
  Usage:
  combatweights <cmd> [field] [value]
  
  Display combatweights, or pass in arguments to change values.
  Values must be floating point numbers.
  cmd can be either set or save.
  """
  if len(args[0]) > 0:
    cmd = args[0]
  else:
    buf = "${blue,bold}Combat Weights:\n"
    keys = combatWeights.keys()
    keys.sort()
    for k in keys:
      buf += "${green,bold}%-20s ${white,bold}= ${yellow,bold}%.2f\n" % (k,combatWeights[k])
    ch.toSelf("%s${reset}" % buf.strip())
    return
    
  if cmd == "set":
    try:
      key = args[1]
      val = float(args[2])
    except:
      ch.interpret("help combatweights")
      return
    
    if key in combatWeights:
      combatWeights[key] = val
      ch.toSelf("Set %s to %.2f." % (key,val))
    else:
      ch.toSelf("Couldn't set %s." % key)
  elif cmd == "save":
    saveCombatWeights()
    ch.toSelf("Combatweights have been saved.")
  else:
    ch.interpret("help combatweights")
    

def healall_command( ch, args ):
  """
  Usage:
  healall
  
  Heals every character in the game.
  """
  for t in ch.d.server.connections.itervalues():
    vict = t.character
    vict.health = vict.maxhealth
    vict.toSelf("You have been healed of all your wounds by %s." % ch.name)


def reportCombat( ch, vict, r, dmg ):
  "Assign damage to vict from ch"
  try:
    if r == "miss":
      ch.toSelf("\nYour attack misses %s." % vict.name)
      vict.toSelf("\n%s's attack misses you." % ch.name)
    elif r == "dodge":
      ch.toSelf("\n%s dodges your attack." % vict.name)
      vict.toSelf("\nYou dodge %s's attack." % ch.name)
    elif r == "parry":
      ch.toSelf("\n%s parries your attack." % vict.name)
      vict.toSelf("\nYou parry %s's attack." % ch.name)
    elif r == "block":
      ch.toSelf("\n%s blocks your attack." % vict.name)
      vict.toSelf("\nYou block %s's attack." % ch.name)
    elif r == "crit":
      critDmg = dmg*combatWeights["crit_mod"]
      ch.toSelf("\nYou ${red,bold}critcally${reset} hit %s for %d damage!" % (vict.name, critDmg) )
      vict.toSelf("\n%s's attack ${red,bold}critcally${reset} hits you for %d damage!." % (ch.name, critDmg) )
      if vict.health > critDmg:
        vict.health -= int(critDmg)
      else:
        vict.health = 1
        vict.toSelf("\nYou have been slain by %s" % ch.name)
        ch.toSelf("\nYou have slain %s!" % vict.name)
        stopCombat( vict, ch )
    elif r == "hit":
      ch.toSelf("\nYou hit %s for %d damage." % (vict.name,dmg) )
      vict.toSelf("\n%s's attack hits you for %d damage." % (ch.name,dmg) )
      if vict.health > dmg:
        vict.health -= int(dmg)
      else:
        vict.health = 1
        vict.toSelf("\nYou have been slain by %s" % ch.name)
        ch.toSelf("\nYou have slain %s!" % vict.name)
        stopCombat( vict, ch )
  except:
    print sys.exc_info()[0]
    print sys.exc_info()[1]
    print traceback.extract_tb(sys.exc_info()[2])
      

def stopCombat( ch, vict=None ):
  """
  Remove ch from combat.
  Also remove vict from combat with ch.
  """
  ch.timers = {}
  ch.combat = False
  rQ = []
  for (c,v,f) in ch.d.server.combats:  
    if v == ch.name or c == ch.name:
      rQ.append((c,v,f))
  for (c,v,f) in rQ:
    ch.d.server.combats.remove((c,v,f))
  if vict is not None:
    for (k,v) in vict.timers.iteritems():
      if k.find(ch.name):
        del vict.timers[k]
    if vict.timers is {}:
      vict.combat = False


def autoAttack( ch, vict ):
  "Registers an auto attack function with the server and puts both parties in combat"
  print "Attempting to put %s into combat with %s." % (ch.name,vict.name)
  
  ch.combat = True
    
  def c():
    try:
      # Initialize the swing timer
      tName = "%s_%s" % (ch.name,vict.name)
      t = time.time()
      
      weaponSpeed = getWeaponSpeed( ch )
      dmg = getWeaponDamage( ch )
      
      if tName not in ch.timers:
        ch.timers[tName] = 0 # start immediately
      
      # Swing away!
      if t >= ch.timers[tName] + weaponSpeed:
        ch.timers[tName] = t 
        r = meleeSwing( generateMeleeCombatTable(ch, vict) )
        reportCombat( ch, vict, r, dmg )
    except:
      print sys.exc_info()[0]
      print sys.exc_info()[1]
      print traceback.extract_tb(sys.exc_info()[2])
        
  ch.d.server.combats.append((ch.name,vict.name,c)) # Put this in the combat loop
  
  # See if the victim has a target. If not, set them up to fight back.
  if not vict.target:
    target( vict, ch )
  if not vict.combat:
    autoAttack( vict, ch )


def swingtest_command( ch, args ):
  """
  Syntax:
  swingtest <victim>
  
  Shows you the combat table for Melee against victim.
  Rolls the dice and displays your hit result.
  """
  try:
    v = args[0]
  except:
    ch.toSelf("You must supply a victim")
    return
  vict = ch.getPlayerInWorld(v)
  t = generateMeleeCombatTable( ch, vict )
  outcome = meleeSwing( t )
  ch.toSelf( "Combat Table:\n %.2f%% miss chance\n %.2f%% dodge chance\n %.2f%% parry chance\n %.2f%% block chance\n %.2f%% crit chance\nSwing outcome: %s" %
              ( t["miss"], t["dodge"], t["parry"], t["block"], t["crit"], outcome ) )


def getWeaponSpeed( ch ):
  if ch.equipped["weapon"] is not None:
    return ch.equipped["weapon"].speed
  else:
    return combatWeights["barehand_speed"]


def getWeaponDamage( ch ):
  if ch.equipped["weapon"] is not None:
    return 1 # fix this later...
  else:
    base = random.randint(combatWeights["barehand_min"],combatWeights["barehand_max"])
    return base + (ch.brawn*combatWeights["brawn_to_dmg"]) 


def target( ch, vict ):
  try:
    ch.target = ch.getPlayerInWorld(vict)
    return ch.target
  except:
    return None
          
          
def target_command( ch, args ):
  """
  Usage:
  target <victim>
  
  Targets a victim.
  """
  try:
    vict = target( ch, args[0] )
    if vict is not None:
      ch.toSelf("Now targeting %s." % vict.name)
    else:
      ch.toSelf("Your target is nowhere to be seen.")
  except:
    print "Couldn't target for some reason..."
    print sys.exc_info()[0]
    print sys.exc_info()[1]
    print traceback.extract_tb(sys.exc_info()[2])


def kill_command( ch, args ):
  """
  Usage:
  kill [victim]
  
  Will attempt to kill a victim.
  """
  if ch.target is not None:
    autoAttack( ch, ch.target )
  else:
    try:
      target( ch, args[0] )
      autoAttack( ch, ch.target )
    except:
      print sys.exc_info()[0]
      print sys.exc_info()[1]
      print traceback.extract_tb(sys.exc_info()[2])
    

def peace_command( ch, args ):
  """
  Usage:
  peace
  
  Stops all combat.
  """
  ch.d.server.combats = []
  for c in ch.d.server.connections.itervalues():
    c.character.combat = False
  ch.toWorld("${yellow,bold}*** Stopping all combat. ***")


def combats_command( ch, args ):
  """
  Usage:
  combats
  
  Displays combat that is currently taking place.
  """
  buf = "Combat:\n"
  for (c,v,f) in ch.d.server.combats:
    buf += "%s is fighting %s\n" % (c,v)
  ch.toSelf(buf)

def timers_command( ch, args ):
    """
    Usage:
    timers
    
    Displays a list of timers.
    """
    ch.toSelf("Timers:\n%s" % str(ch.d.server.timers))

def generateMeleeCombatTable( ch, vict ):
  "Build a melee combat table between two characters"
  retTable = {}
  retTable["miss"]  = 5 # 5% base chance to miss
  retTable["dodge"] = 5 # 5% base chance to dodge
  retTable["parry"] = 0 # 0% base chance to parry
  retTable["block"] = 0 # 0% base chance to block
  retTable["crit"]  = 5 # 5% base chance to crit
  
  # the only way to reduce miss chance is for ch to have bonuses to hit% chance
  # either magically or through weapon skill
  # there will always be a 1% chance to miss
  if ch.toHit:
    tmp = retTable["miss"] - ch.toHit
    if tmp < 1:
      retTable["miss"] = 1
    else:
      retTable["miss"] = tmp
    
  if ch.toCrit:
    retTable["crit"] += ch.toCrit
  
  if vict.alacrity > 0:
    retTable["dodge"] += vict.alacrity*combatWeights["alacrity_to_dodge"]

  if ch.toParry:  
    retTable["parry"] += ch.toParry

  if ch.toBlock:
    retTable["block"] += ch.toBlock
    
  return retTable
    
def meleeSwing( combatTable ):
  "Determine the outcome of a melee swing in one roll"
  roll = random.random()*100
  
  if roll < combatTable["miss"]:
    return "miss"
  elif roll < (combatTable["dodge"]+combatTable["miss"]):
    return "dodge"
  elif roll < (combatTable["parry"]+combatTable["dodge"]+combatTable["miss"]):
    return "parry"
  elif roll < (combatTable["block"]+combatTable["parry"]+combatTable["dodge"]+combatTable["miss"]):
    return "block"
  elif roll < (combatTable["crit"]+combatTable["block"]+combatTable["parry"]+combatTable["dodge"]+combatTable["miss"]):
    return "crit"
  else:
    return "hit"
